#ifndef LOCAL_FS_H
#define LOCAL_FS_H

#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
#include <utility>
#include <string_view>
#include <map>

#include <windows.h>

#include "api/gfs.h"
#include "VioFsAPI.h"
#include "fuse.h"

struct SubmitVioFsRequestHeader {
    struct fuse_in_header   hdr;
    struct GuestFuseSrvRPL  rpl;
};

struct GuestFuseRPLEntry {
    uint64_t      opid;
    int64_t       offset;
    uint32_t      opcode;
    uint32_t      size;
    int32_t       handle;
    uint32_t      pad[1];
};

class LocalFS {
public:
    LocalFS();
    ~LocalFS();
    BOOL Initialize();
    int CheckShareInUse(const std::string& shareName);
    void ShareCleanup(const std::string& shareName);
    void RemoveAllFileHandle();

    int Open(const char* fullPath,
             GuestFuseSrvFileMetaData& fileMetaData,
             GuestFuseSrvRPL* rpl,
             bool readOnly,
             const std::string& shareName);
    int Release(const char* fullPath,
                GuestFuseSrvFileMetaData& fileMetaData,
                GuestFuseSrvRPL* rpl,
                const std::string& shareName);
    int GetAttr(const char* fullPath, GuestFuseSrvRPL* rpl);
    int Read(const char* fullPath,
             GuestFuseSrvFileMetaData& fileMetaData,
             GuestFuseSrvRPL* rpl,
             const std::string& shareName);
    int Write(const char* fullPath,
              GuestFuseSrvFileMetaData& fileMetaData,
              const char* data,
              GuestFuseSrvRPL* rpl,
              const std::string& shareName);
    int ReadDir(const char* fullPath, GuestFuseSrvRPL* rpl, int rplBuffSz);
    int Rename(const char* fullPath, const char* newFullPath);
    int Remove(const char* fullPath);
    int Truncate(const char* fullPath, GuestFuseSrvFileMetaData& fileMetaData);
    int MkDir(const char* fullPath);
    int RmDir(const char* fullPath);

private:
    BOOL m_bInitialized;
    VioFsAPI m_vioFs;
    SubmitVioFsRequestHeader* m_vioFsRequestBuf;
    std::mutex m_queueGuard;
    std::queue<GuestFuseRPLEntry> m_queue;
    std::atomic<BOOL> m_exit;
    std::thread m_loop;
    std::condition_variable m_cond;
    WCHAR* m_path;
    std::mutex m_localFSGuard;
    std::map<std::string, std::map<std::pair<std::wstring_view, uint64_t>, int>> m_openedFileHandle;

    int GetFileHandle(const WCHAR* path,
                      uint64_t hostFileHandle,
                      const std::string& shareName);
    void AddNewFileHandle(const WCHAR* path,
                          uint64_t hostFileHandle,
                          int localFileHandle,
                          const std::string& shareName);
    int RmFilehandle(const WCHAR* path,
                     uint64_t hostFileHandle,
                     const std::string& shareName);

    INT ReadFile(int32_t fileHandle, int64_t offset, uint32_t readSize, LPVOID buffer);
    INT HandleGuestFuseRPLProcess(GuestFuseRPLEntry& entry, LPVOID buffer);
    void HandleGuestFuseRPLLoop();
    void AddGuestFuseRPLToQueue(GuestFuseRPLEntry& inHeader);
};

#endif // LOCAL_FS_H
